#ifndef SPW_SHA_PTR_H_
#define SPW_SHA_PTR_H_

#include <typeinfo>
#include <type_traits>

#include <exception/spw_exception.h>
#include <spw_config.h>
#include <utility/spw_move_in.h>
#include <memory/spw_alloc.h>
#include <memory/spw_uni_ptr.h>
/*
* 不支持多线程, 没有锁和原子
* share_ptr 内有两个指针
* 一个是所指对象的指针
* 一个是指向控制块的指针, 控制块包括：所指对象指针, 引用计数，可能有的deleter or allocater
* deleter回收指针所指内存，allocater分配控制块内存大小
* 本代码中只能支持==或!=比较
* 
* 想法[根据stl源码]：
* 1. allocator 参数主要作用是分配控制块的内存。控制块使用相对较少，所占内存也小，使用自己定制allocator
*    做参数相对较少.
*/

// 异常BadWeakPtr
namespace spwstd{
namespace spwexcept{
class BadWeakPtr: public spwstd::spwexcept::Exception {
public:
    virtual const char* What() const noexcept  {
        return "spwstd::spwexcept::BadWeakPtr";
    } 
    virtual ~BadWeakPtr() = default;
};
}
}
namespace spwstd{
namespace spwptr{

// ## 引用计数, 抽象类
class CountBase {
public:
    CountBase(): use_count_(1), weak_count_(1) {}
    virtual ~CountBase() {};
    // use_count_ == 0
    virtual void Dispose() = 0;
    // weak_count_ == 0, 该对象是动态分配的
    virtual void Destroy() { delete this; }
    // 多态要准确知道对象的deleter
    virtual void* GetDeleter(const std::type_info& ) = 0;

    void AddRefCopy() { ++use_count_; }
    void Release() {
        if(--use_count_ == 0) {
            Dispose();
            if(--weak_count_ == 0) {
                Destroy();
            }
        }
    }

    void WeakAddRefCopy() { ++weak_count_; }
    void WeakRelease() { 
        if(--weak_count_ == 0) {
            Destroy();
        }
    }

    int GetCounts() const { return use_count_; }

private:
    int use_count_;
    int weak_count_;
};

// 向前声明
class WeakCount;

template<typename T>
class WeakPtr;
// ## 控制块核心
// ### 1.没有Deleter和allocater 
// 当shareptr使用已有的指针构造时，会调用该控制块
template<typename P>
class CountPtr final: public CountBase {
public:
    explicit CountPtr(P ptr): ptr_{ptr} {}
    virtual void Dispose() { delete ptr_; }
    virtual void Destroy() { delete this; }
    virtual void* GetDeleter(const std::type_info&) { return nullptr; }

    // 禁止复制
    CountPtr(const CountPtr&) = delete;
    CountPtr& operator=(const CountPtr&) = delete;
private:
    P ptr_;
};

template<>
inline void CountPtr<spwconfig::nullptr_t>::Dispose() {}

// ### 2.有Deleter和allocater 
// 当shareptr使用参数含有deleter or/and allocater时，使用该控制块
// 因为deleter和allocater通常是空基类，所以使用EBO空基类优化
template<typename T, 
    bool use_ebo = !std::is_final_v<T> && std::is_empty_v<T>>
struct EboHelper_in;

template<typename T>
struct EboHelper_in<T, false>{
    explicit EboHelper_in(const T& m): m_(m){}
    static T& Get(EboHelper_in& ebo) { return ebo.m_; }
private:
    T m_;
};

template<typename T>
struct EboHelper_in<T, true> : private T{
    explicit EboHelper_in(const T& m): T(m){}
    static T& Get(EboHelper_in& ebo) { return static_cast<T&>(ebo);}
};

template<typename Ptr, typename Del, typename Alloc>
class DelAllocCount final : public CountBase {
    class Impl : EboHelper_in<Del>, EboHelper_in<Alloc>
    {
        using DelBase = EboHelper_in<Del>;
        using AllocBase = EboHelper_in<Alloc>;
    public: 
        Impl(Ptr p, Del d, const Alloc& a): DelBase(d), AllocBase(a), ptr_(p) {}
        Del& GetDel() { return DelBase::Get(*this); }
        Alloc& GetAlloc() { return AllocBase::Get(*this); }
        Ptr ptr_;
    };

public:
    using AllocType = spwdetail::AllocRebind<Alloc, DelAllocCount>;

    DelAllocCount(Ptr p, Del d): impl_(p, d, Alloc()) {}
    DelAllocCount(Ptr p, Del d, const Alloc& a): impl_(p, d, a) {}

    ~DelAllocCount() {
    }

    virtual void Dispose() {
        // 使用deleter删除所指对象
        impl_.GetDel()(impl_.ptr_);
    }

    // 处理分配的控制块内存
    virtual void Destroy() {
        // 析构
        this->~DelAllocCount();
        // 把用Alloc分配器分配的内存释放
        // 为什么在sharecount中申请动态内存，而在这里释放动态内存?
        // 答：分配的动态内存只有一份即，DelAllocCount,MakeSharedCount,CountPtr这三个
        // 控制块中的一份。而每个shareptr都有一个sharecount, sharecount其内部指针指向控制块
        // 当第一次构造shareptr时，会分配控制块大小动态内存, 当一个shareptr销毁且不是最后一个的话
        // 控制块中的计数引用只会-1. 直到引用计数use_count为0且引用计数weak_count为0才会,
        // 即没有shareptr和weakptr指向该对象时才销毁控制块
        spwstd::AllocInterface<DelAllocCount, AllocType>::s_deallocate(this);
    }

    virtual void* GetDeleter(const std::type_info& t) {
        return t == typeid(Del) ? spwuti::addressOf(impl_.GetDel()) : nullptr;   
    }

private:
    Impl impl_;
};

// ### 3.makeshare, 控制块和对象一起申请内存
// 当使用makeshare构造时，使用该控制块
struct MakeSharedTag {};

template<typename T, typename Alloc>
class MakeSharedCount final: public CountBase{
    // sizeof(Impl) = sizeof(Alloc) + sizeof(T)
    class Impl: EboHelper_in<Alloc> {
        using AllocBase = EboHelper_in<Alloc>;
    public:
        template<typename... Args>
        explicit Impl(Alloc a, Args&&... args): AllocBase(a), storage_{args...} {
        }

        Alloc& GetAlloc() { return AllocBase::Get(*this); }
        T storage_;
    };

public:
    using AllocType = spwdetail::AllocRebind<Alloc, MakeSharedCount>;

    template<typename... Args>
    MakeSharedCount(Alloc a, Args&&... args): impl_(a, args...) {
    }

    ~MakeSharedCount() {
    }

    virtual void Dispose() {
        // 析构storage_, 释放所指对象内存
        //spwstd::AllocInterface<T>::destroy(getStoragePtr());
        impl_.storage_.~T();
    }

    //  释放析构分配控制块内存 
    virtual void Destroy() {
        // 析构该控制块
        this->~MakeSharedCount();
        // 把用Alloc分配器分配的内存释放
        spwstd::AllocInterface<MakeSharedCount, AllocType>::s_deallocate(this);
    }

    // 没有deleter函数, 返回nullptr, 或返回所指对象指针地址
    virtual void* GetDeleter(const std::type_info& t) {
        if(t == typeid(MakeSharedTag))
            return const_cast<typename std::remove_cv<T>::type*>(GetStoragePtr());
        return nullptr;
    }

private:
    T* GetStoragePtr() { return spwuti::addressOf(impl_.storage_); }
    Impl impl_;
};

// ## 控制块
// ### sharecount, shareptr的控制块
class ShareCount{
public:
    constexpr ShareCount(): count_base_{nullptr} {}

    // 原本有内存指针, 且没有deleter or/ane allocator, 使用CountPtr
    template<typename Ptr>
    explicit ShareCount(Ptr p): count_base_{nullptr} {
        try {
            count_base_ = new CountPtr<Ptr>(p);
        } catch (...) {
            delete p;
            throw;
        }
    }

    // 有deleter or/and allocator, 使用DelAllocCount
    template<typename Ptr, typename Del>
    ShareCount(Ptr p, Del d): ShareCount{p, spwuti::move(d), spwstd::DefaultAlloc<void>()} {}

    template<typename Ptr, typename Del, typename Alloc>
    ShareCount(Ptr p, Del d, Alloc a): count_base_{nullptr}
    {
        // Alloc<DelAllocCount<>,Alloc>
        using RealAllocType = typename DelAllocCount<Ptr, Del, Alloc>::AllocType;
        using AllocInter =  AllocInterface<DelAllocCount<Ptr, Del, RealAllocType>, RealAllocType>;

        try {
        // 使用分配器分配 DelAllocCount大小内存并构造, 注意这里使用了动态分配，记得回收内存
            auto ptr = AllocInter::s_allocate();
            AllocInter::s_construct(ptr, p, spwuti::move(d), spwuti::move(a));
            count_base_ = ptr;
        } catch(...) {
            // 控制块内存分配失败，deleter 所指对象
            d(p);
            throw;
        }
    }

    // makeshare 使用 MakeSharedCount
    template<typename T, typename Alloc, typename... Args>
    ShareCount(MakeSharedTag, T*, const Alloc& a, Args&&... args):count_base_{nullptr} {
        // Alloc<MakeSharedCount<>,Alloc>
        using RealAllocType = typename MakeSharedCount<T, Alloc>::AllocType;
        using AllocInter =  AllocInterface<MakeSharedCount<T, RealAllocType>, RealAllocType>;
        // 动态分配了内存，记得delete
        auto ptr = AllocInter::s_allocate();
        AllocInter::s_construct(ptr, spwuti::move(a), args...);
        count_base_ = ptr;
    }

    // 使用unique_ptr构造, unique_ptr没有Alloc, 因为它没有控制块
    template<typename T, typename Del>
    explicit ShareCount(UniquePtr<T, Del>&& r)
    : count_base_{nullptr} 
    {
        using Ptr = typename UniquePtr<T, Del>::Pointer;
        // 不支持引用类型
        using Del2 = typename std::remove_reference<Del>::type;

        using RealAllocType = typename DelAllocCount<Ptr, Del2, DefaultAlloc<void>>::AllocType;
        using AllocInter =  AllocInterface<DelAllocCount<Ptr, Del2, RealAllocType>, RealAllocType>;

        auto ptr = AllocInter::s_allocate();
        AllocInter::s_construct(ptr, r.Release(), r.GetDeleter());
        count_base_ = ptr;
    }

    // 使用weakcout构造
    explicit ShareCount(const WeakCount& r);

    ~ShareCount() {
        if(count_base_ != nullptr) {
            count_base_->Release();
        }
    }

    ShareCount(const ShareCount& r): count_base_{r.count_base_} {
        if(count_base_ != nullptr)
            count_base_->AddRefCopy();
    }

    ShareCount& operator=(const ShareCount& r) {
        CountBase* tmp = r.count_base_;
        if(tmp != count_base_){
            if(tmp != nullptr)
                tmp->AddRefCopy();
            if(count_base_ != nullptr)
                count_base_->Release();
            count_base_ = tmp;
        }
        return *this;
    }

    void Swap(ShareCount& r){
        CountBase* tmp = r.count_base_;
        r.count_base_ = count_base_;
        count_base_ = tmp;
    }

    int GetCounts() const {
        return count_base_ != nullptr ? count_base_->GetCounts() : 0;
    }

    void* GetDeleter(const std::type_info& t) const {
        return count_base_ ? count_base_->GetDeleter(t) : nullptr;
    }

    bool IsUnique() const { return this->GetCounts() == 1; }

    CountBase* GetPtr() const { return count_base_; }

private:
    CountBase* count_base_;
};

inline bool operator==(const ShareCount& a, const ShareCount& b) {
    return a.GetPtr() == b.GetPtr();
}

// ### weakcount, weakcount的控制块
// 该控制块支持移动
class WeakCount{
public:
    constexpr WeakCount(): count_base_{nullptr} {}

    WeakCount(const ShareCount& r): count_base_{r.GetPtr()} 
    {
        if(count_base_ != nullptr)
            count_base_->WeakAddRefCopy();
    }

    WeakCount(const WeakCount& r): count_base_{r.count_base_} 
    {
        if(count_base_ != nullptr)
            count_base_->WeakAddRefCopy();
    }

    WeakCount(WeakCount&& r): count_base_{r.count_base_}
    {
        r.count_base_ = nullptr;
    }

    WeakCount& operator=(const ShareCount& r){
        CountBase* tmp = r.GetPtr();
        if(tmp)
            tmp->WeakAddRefCopy();
        if(count_base_)
            count_base_->WeakRelease();
        count_base_ = tmp;
        return *this;
    }

    WeakCount& operator=(const WeakCount& r){
        CountBase* tmp = r.count_base_;
        if(tmp)
            tmp->WeakAddRefCopy();
        if(count_base_)
            count_base_->WeakRelease();
        count_base_ = tmp;
        return *this;
    }

    WeakCount& operator=(WeakCount&& r){
        if(count_base_ != nullptr)
            count_base_->WeakRelease();
        count_base_ = r.count_base_;
        r.count_base_ = nullptr;
        return *this;
    }

    ~WeakCount(){
        if(count_base_ != nullptr){
            count_base_->WeakRelease();
        }
    }

    void Swap(WeakCount& r){
        CountBase* tmp = r.count_base_;
        r.count_base_ = count_base_;
        count_base_ = tmp;
    }

    int GetCounts() const {
        return count_base_ != nullptr ? count_base_->GetCounts() : 0;
    }


    CountBase* GetPtr() const { return count_base_; }

private:
    CountBase* count_base_;
};

inline bool operator==(const WeakCount& a, const WeakCount& b) {
    return a.GetPtr() == b.GetPtr();
}

inline ShareCount::ShareCount(const WeakCount& r): count_base_{r.GetPtr()}
{
    if(count_base_!=nullptr){
        // r.getCounts==0 说明原来指针已经空挂无效
        if (r.GetCounts()==0)
            throw spwstd::spwexcept::BadWeakPtr();
        count_base_->AddRefCopy();
    } else {
        throw spwstd::spwexcept::BadWeakPtr();
    }
}

// # shareptr
template<typename T>
class SharePtr{
    template<typename Ptr>
    using Convertible = 
        typename std::enable_if<
            std::is_convertible<Ptr, T*>::value>::type; 
public:
    using ElementType = T;

    constexpr SharePtr(): ptr_(nullptr), countptr_() {}

    template<typename T1, typename = Convertible<T1*> >
    explicit SharePtr(T1* p): ptr_(p), countptr_(p) {}

    template<typename T1, typename Del, typename = Convertible<T1*> >
    SharePtr(T1* p, Del d): ptr_(p), countptr_(p, d) {}

    template<typename T1, typename Del, typename Alloc, typename = Convertible<T1*> >
    SharePtr(T1* p, Del d, Alloc a): ptr_(p), countptr_(p, d, spwuti::move(a)) {}

    template<typename Del>
    SharePtr(spwconfig::nullptr_t p, Del d): ptr_(nullptr), countptr_(p, d) {}

    template<typename Del, typename Alloc>
    SharePtr(spwconfig::nullptr_t p, Del d, Alloc a): ptr_(nullptr), countptr_(p, d, spwuti::move(a)) {}

//使用一个已有shareptr中控制模块和一个新ptr, 创建一个shareptr, 与已有shareptr共享控制块  
// 别名构造函数
    template<typename T1>
    SharePtr(const SharePtr<T1>& r, T* p): ptr_(p), countptr_(r.countptr_){}

    SharePtr(const SharePtr&) = default;
    SharePtr& operator=(const SharePtr&) = default;
    ~SharePtr() = default;

    template<typename T1, typename = Convertible<T1*>>
    SharePtr(const SharePtr<T1>& r): ptr_{r.ptr_}, countptr_{r.countptr_} {}

    SharePtr(SharePtr&& r):ptr_{r.ptr_}, countptr_{} 
    {
        countptr_.Swap(r.countptr_);
        r.ptr_ = nullptr;
    }

    template<typename T1, typename = Convertible<T1*>>
    SharePtr(SharePtr<T1>&& r):ptr_{r.ptr_}, countptr_{} 
    {
        countptr_.Swap(r.countptr_);
        r.ptr_ = nullptr;
    }

    template<typename T1>
    explicit SharePtr(const WeakPtr<T1>& r)
    : countptr_(r.countptr_) 
    {
        ptr_ = r.ptr_;
    }

    // 从uniqueptr构造
    template<typename T1, typename Del, 
        typename = Convertible<typename UniquePtr<T1,Del>::Pointer> >
    SharePtr(UniquePtr<T1, Del>&& r): ptr_{r.Get()}, countptr_{}
    {
        countptr_ = ShareCount(spwuti::move(r));
    }

    constexpr SharePtr(spwconfig::nullptr_t): SharePtr() {}

    // operator=
    template<typename T1>
    SharePtr& operator=(const SharePtr<T1>& r) {
        ptr_ = r.ptr_; // T = T1
        countptr_ = r.countptr_;
        return *this;
    } 

    SharePtr& operator=(SharePtr&& r) {
        // 把r move给该shareptr
        SharePtr(spwuti::move(r)).Swap(*this);
        return *this;
    }

    template<typename T1>
    SharePtr& operator=(SharePtr<T1>&& r) {
        // 把r move给该shareptr
        SharePtr(spwuti::move(r)).Swap(*this);
        return *this;
    }

    template<typename T1, typename Del>
    SharePtr& operator=(UniquePtr<T1, Del>&& r) {
        // 把r move给该shareptr
        SharePtr(spwuti::move(r)).Swap(*this);
        return *this;
    }

    void Reset() {
        ptr_ = nullptr;
        countptr_ = ShareCount{};
    }

    template<typename T1>
    void Reset(T1* p) {
        #ifdef USEDEBUG
        assert(p==nullptr || p!=ptr_, "p!=nullptr and p==ptr_, ");
        #endif
        ptr_ = p;
        countptr_ = ShareCount(p);
    }

    template<typename T1, typename Del>
    void Reset(T1* p, Del d) {
        ptr_ = p;
        countptr_ = ShareCount(p, d);
    }

    template<typename T1, typename Del, typename Alloc>
    void Reset(T1* p, Del d, Alloc a) {
        ptr_ = p;
        countptr_ = ShareCount(p, d, spwuti::move(a));
    }

    typename std::remove_reference<T>::type&
    operator*() const {
        #ifdef USEDEBUG
        assert(ptr_ != nullptr);
        #endif
        return *ptr_; 
    }  

    T* operator->() const {
        #ifdef USEDEBUG
        assert(ptr_ != nullptr);
        #endif
        return ptr_; 
    }

    T* Get() const {
        return ptr_;
    }

    explicit operator bool() const {
        return ptr_ == nullptr ? false : true;
    }

    bool IsUnique() const {
        return countptr_.IsUnique();
    }

    int GetCounts() const {
        return countptr_.GetCounts();
    }

    void Swap(SharePtr<T>& other) {
        spwuti::swap(ptr_, other.ptr_);
        countptr_.Swap(other.countptr_);
    }

template<typename Alloc, typename... Args>
SharePtr(MakeSharedTag tag, const Alloc& a, Args&&... args)
: ptr_{}, countptr_{tag, reinterpret_cast<T*>(0), a, spwuti::forward<Args>(args)...}
{
    ptr_ = static_cast<T*>(countptr_.GetDeleter(typeid(tag)));
}

private:
    template<typename T1> friend class WeakPtr;
    template<typename T1> friend class SharePtr;

    T* ptr_;
    ShareCount countptr_;
};

// # weakptr
template<typename T>
class WeakPtr{
    template<typename Ptr>
    using Convertible = 
        typename std::enable_if<
            std::is_convertible<Ptr, T*>::value>::type; 
public:
    using ElementType = T;

    constexpr WeakPtr(): ptr_(nullptr), countptr_() {} 

    WeakPtr(const WeakPtr&) = default;
    ~WeakPtr() = default;

    template<typename T1, typename = Convertible<T1*>>
    WeakPtr(const WeakPtr<T1>& r): countptr_(r.countptr_)
    {
        ptr_ = r.ptr_;
    }

    template<typename T1, typename = Convertible<T1*>>
    WeakPtr(const SharePtr<T1>& r): ptr_(r.Get()),countptr_(r.countptr_)
    {
    }

    WeakPtr(WeakPtr&& r):ptr_(r.ptr_),countptr_(spwuti::move(r.countptr_)) {
        r.ptr_ = nullptr;
    }

    template<typename T1, typename = Convertible<T1*>>
    WeakPtr(WeakPtr<T1>&& r): ptr_(r.ptr_),countptr_(spwuti::move(r.countptr_)) {
        r.ptr_ = nullptr;
    }

    WeakPtr& operator=(const WeakPtr& r) = default;

    template<typename T1>
    WeakPtr& operator=(const WeakPtr<T1>& r) {
        ptr_ = r.ptr_;
        countptr_ = r.countptr_;
        return *this;
    }

    template<typename T1>
    WeakPtr& operator=(const SharePtr<T1>& r) {
        ptr_ = r.Get();
        countptr_ = r.countptr_;
        return *this;
    }

    WeakPtr& operator=(WeakPtr&& r) {
        ptr_ = r.ptr_;
        r.ptr_ = nullptr;
        countptr_ = spwuti::move(r.countptr_);
        return *this;
    }

    template<typename T1>
    WeakPtr& operator=(WeakPtr<T1>&& r) {
        ptr_ = r.ptr_;
        r.ptr_ = nullptr;
        countptr_ = spwuti::move(r.countptr_);
        return *this;
    }

    SharePtr<T> Lock() const {
        return Expired()? SharePtr<T>() : SharePtr<T>(*this);
    }

    int GetCounts() const {
        return countptr_.GetCounts();
    }

    bool Expired() const {
        return countptr_.GetCounts() == 0;
    }

    void Reset() {
        ptr_ = nullptr;
        countptr_ = WeakCount();
    }

private:
    template<typename T1> friend class SharePtr;
    template<typename T1> friend class WeakPtr;

    T* ptr_;
    WeakCount countptr_;
};

// # enablesharedfromthis
// 防止同一个指针，被不同shareptr包含, 导致多重控制块的情况
template<typename T>
class EnableSharedFromThis{
protected:
    constexpr EnableSharedFromThis(){}
    EnableSharedFromThis(const EnableSharedFromThis&) {}
    EnableSharedFromThis& operator=(const EnableSharedFromThis&)
    { return *this; }
    ~EnableSharedFromThis(){}

public:
    SharePtr<T> shareFromThis()
    {
        return SharePtr<T>(this->weakptr_);
    }

    SharePtr<const T> shareFromThis () const
    {
        return SharePtr<const T>(this->weakptr_);
    }

private:
    mutable WeakPtr<T> weakptr_;
};

// # makeshare/allocshare
template<typename T, typename Alloc, typename... Args>
inline SharePtr<T> allocateShared(const Alloc& a, Args&&... args)
{
    return SharePtr<T>(MakeSharedTag(), a, spwuti::forward<Args>(args)...);
}

template<typename T, typename... Args>
inline SharePtr<T> makeShared(Args&&... args)
{
    using Type_noc = typename std::remove_const<T>::type;
    return allocateShared<T>(spwstd::DefaultAlloc<Type_noc>(), spwuti::forward<Args>(args)...);
}
} // namespace spwptr 
} // namespace spwstd
#endif // SPW_SHA_PTR_H_
